<html>
<head>
<title>Game Fifteen</title>
<style>
body{background:lightgray;color:black;}
td{border:2px solid black;font-size:2em;width:50px;height:50px;color:white;background:gray;}
.empty{border:0;background:none;}
table{border:0;background:orange;margin:50px;}
button{background:orange;color:black;}
.inputs{padding-top:5px;}
.hidden{display:none;}
#winMsg{border:2px solid green;color:green;padding:5px;margin-bottom:10px;background:white;}
</style>
</head>
<body>
<script>
function Game15(difficulty){
this.f=[
[1,2,3,4],
[5,6,7,8],
[9,10,11,12],
[13,14,15,0]];

var size=4;

var self=this;

this.num=function(rc){
var r=rc.r;
var c=rc.c;
if(r<0 || c<0 || r>=size || c>=size){
   return NaN;
}
return ((c%size + r*size)+1)%(size*size);
};//end this.num

this.isSolved=function(){
for(var i=0;i<size;i++)
for(var j=0;j<size;j++){
   if(self.num({r:i,c:j}) != self.f[i][j])
   return false;
}
return true;
};//end this.isSolved

this.find=function(n){
for(var i=0;i<size;i++)
for(var j=0;j<size;j++){
   if(n == self.f[i][j])
   return {r:i,c:j};
}
return false;
};//end this.find

this.zero=function(){
return self.find(0);
};//end this.zero

this.move=function(num){
   var z=self.zero();
   var n=self.find(num);
   if(!n || !z) return false;

   var dif = Math.abs(n.r-z.r)+Math.abs(n.c-z.c);
   if(dif != 1) return false;

   self.f[z.r][z.c]=self.f[n.r][n.c];
   self.f[n.r][n.c]=0;
   return {z:n,n:z};
};//end this.move

this.getZeroAndCo=function(){
   var n=[];
   var z=self.zero();
   n.push(z);
   if(z.r+1<size) n.push({r:z.r+1,c:z.c});
   if(z.c+1<size) n.push({r:z.r,c:z.c+1});
   if(z.r-1>=0) n.push({r:z.r-1,c:z.c});
   if(z.c-1>=0) n.push({r:z.r,c:z.c-1});

   return n;
};

this.randMove=function(){
   var a=self.getZeroAndCo();
   var max=a.length -1;
   var i = (parseInt(Math.random()*max)) + 1;
   var z=a[0];
   var n=a[i];
   self.f[z.r][z.c]=self.f[n.r][n.c];
   self.f[n.r][n.c]=0;
};

for(var c=0;c<difficulty;c++)
   this.randMove();

};//End Game15

function Game15UI(){
var self=this;
var size=4;

this.difficulty=0;
this.moves=0;

this.wl=function(t){
document.write(t+"<br />");
};

this.id=function(id){
return document.getElementById(id);
};

this.cl=function(id,className){
self.id(id).className=className;
};

this.setEmpty=function(i,j){
self.cl("t"+i+j, "empty");
};

this.removeEmpty=function(i,j){
self.cl("t"+i+j, "");
};

this.val=function(num){
return (num || "");
};

this.put=function(id,html){
self.id(id).innerHTML=html;
};

this.setDiff=function(diff){
self.difficulty=diff;
self.put("difficulty", "Game difficulty: "+self.difficulty);
};

this.setMoves=function(m){
self.moves=m;
self.put("moves", "Moves made: "+self.moves);
};

this.g=new Game15(0);

this.setGame=function(diff){
   self.g=new Game15(diff);
   while(self.g.isSolved())
      self.g=new Game15(diff);
   self.setDiff(diff);
   self.setMoves(0);
};

this.set=function(i,j,num){
self.put("t"+i+j, self.val(num));
};

this.solved=function(){
   var msg="Congratulations! You have solved the puzzle with difficulty "+self.difficulty+" in "+self.moves+(self.moves==1?" move!":" moves!");
   alert(msg);
   self.put("winMsg", msg);
   self.cl("winMsg", "");
   self.setDiff(0);
   self.setMoves(0);
};

this.ui=function(id){
var html="";
html+="<table>";

for(var i=0;i<size;i++){
html+="<tr>";
for(var j=0;j<size;j++){
   html+="<td id='t"+i+j+"'>"+self.val(self.g.f[i][j])+"</td>";
}
html+="</tr>";
}

html+="</table>";

self.put(id,html);

var z=self.g.zero();
self.setEmpty(z.r, z.c);
self.setDiff(self.difficulty);
self.setMoves(self.moves);
};//end this.ui

this.newGame=function(inp){
   var num=parseInt(inp);
   if(isNaN(num) || num<1){
      alert("Invalid input: "+inp+"! Try entering positive integer!");
   return false;
   }
   
   self.id('inp').value="";
   self.setGame(num);

   self.ui("game15");
   self.put("winMsg", "");
   self.cl("winMsg", "hidden");

};//end this.newGame

this.play=function(el){
var num = parseInt(el.innerHTML);
if(!isNaN(num)){
   if(num>0 && num<16){
      var zn = self.g.move(num);
      if(zn){
      self.set(zn.z.r, zn.z.c, 0);
      self.set(zn.n.r, zn.n.c, num);
      
      self.setEmpty(zn.z.r, zn.z.c);
      self.removeEmpty(zn.n.r, zn.n.c);

      if(self.difficulty>0)
         self.setMoves(self.moves+1);

      if(self.g.isSolved() && self.difficulty>0) self.solved();
     }
   }
}
};//end this.play

};//End Game15UI

var d=new Game15UI();
</script>

<div id="difficulty"></div>
<div id="moves"></div>
<div id="game15" onclick="d.play(window.event.srcElement)">FATAL ERROR</div>
<div>

<div id="winMsg" class="hidden"></div>

<div>Input positive integer for game difficulty</div>

<div class="inputs">
<input type="text" id="inp" class="free"/>
<button  onclick="d.newGame(d.id('inp').value)" id="btn" class="btn">Start new game</button>
</div>

</div>

<script>
d.ui("game15");
</script>
</body>
</html>